<?php
class SqlParser extends Base {
	private static $_templateSql = 'SELECT :distinct :field FROM :table :where :order :group';
	private $_sql = '';
    private $_compExp = array(
        'neq' => '!=',
        'gte' => '>=',
        'lte' => '<=',
        'notlike' => 'NOT LIKE',
        'gt' => '>',
        'lt' => '<',
        'eq' => '=',
        'like' => 'LIKE'
    );
    /**
     * 查询
     * @param array $options
     */
	public function select(Array $options) {
		$this->_sql = str_replace(array(
			':distinct',':field',':table',':where',':group',':order'
			),array(
			$this->_distinct($options['distinct']),
			$this->_field($options['field']),
			$this->_table($options['table']),
			$this->_where($options['where']),
			$this->_group($options['group']),
			$this->_order($options['order'])
			),self::$_templateSql);
	}
    /**
     * 修改
     * @param array $data
     * @param array $options
     */
    public function update($data, $options){

        $this->_sql = "UPDATE "
            . $this->_table($options['table'])
            . $this->_set($data)
            . $this->_where($options['where']);
    }

    /**
     * 添加
     * @param array $data
     * @param array $options
     */
    public function insert($data, $options){

        $this->_sql = "INSERT INTO "
            . $this->_table($options['table'])
            . $this->_insert($data)
            . $this->_where($options['where']);
    }

    /**
     * 删除
     * @param array $options
     */
    public function delete($options){
        $this->_sql = "DELETE FROM "
            . $this->_table($options['table'])
            . $this->_where($options['where']);
    }

	public function getSql() {
		return $this->_sql;
	}

	private function _distinct($distinct) {
		return empty($distinct) ? '' : 'DISTINCT';
	}

	/**
	 * 然后是字符串，这种的处理很简单，直接返回就好，如$this->field('uid,password')，
	 * 传递的就是字符串'uid,password'，这种实际上不太好。
	 *
	 * 然后就是传递数组，首先是array('uid','password')这种方式，
	 * 这种也需要foreach一下，然后进行字符串拼接即可，元素之间通过，分隔。
	 * 如果传递的是array('uid' => 'test','password' => 'test') 那么解析之
	 * 后就要变成'uid AS test,password AS test'，所以拼接的时候需要传递AS子串。
	 * Enter description here ...
	 * @param array $field
     * @return string fields
	 */
	private function _field($field) {
		if(is_array($field)) {
			$arr = array();
			foreach($field as $key => $val) {
				if(is_numeric($key)) {
					$arr[] = $val;
				} else {
					$arr[] = ($key . ' AS ' . $val);
				}
			}
			return implode(',',$arr);
		} elseif(is_string($field) && !empty($field)) {
			return $field;
		} else {
			return '*';
		}
	}
	private function _table($table) {
		if(is_array($table)) {
			$arr = array();
			foreach($table as $key => $val) {
				if(is_numeric($key)) {
					$arr[] = $val;
				} else {
					$arr[] = ($key . ' AS ' . $val);
				}
			}
			return implode(',',$arr);
		} else {
			return $table;
		}
	}
	/**
	 * 有复杂 明天继续
	 * <a href="http://my.oschina.net/mingtingling/blog/98233">(怎么一步步编写简单的PHP的Framework（二十一）)</a>
	 * 
	 * @param array|string $where
     * @return string
	 */
    private function _where($where) {

        if(empty($where)) {
            return ' ';
        }
        if(is_string($where)) {
            return ' WHERE ' . $where;
        }elseif(is_array($where)) {
            $str = ' WHERE ';
            $arrLen = count($where);
            $count = 0;
            foreach($where as $key => $val) {
                $needJoinStr = true;
                $joinStr = ' AND ';
                $val = preg_split('/\s{1,}/i',$val);
                //print_r($val);
                if(preg_match('/^lt|lte|gt|gte|eq|neq|like|notlike$/i',$val[0])) {
                    $command = $this->_parseCompExp($val[0]);
                    $str .= (' ' . $key . ' ' . $command . ' \'' . $val[1] . '\'');
                } elseif('in' === strtolower($val[0])) {
                    if(is_array($val[1])) {
                        $str .= (' ( ' . $key . ' IN ' . implode(',',$val[1]) . ')');
                    } else {
                        $str .= (' ( ' . $key . ' IN ' . $val[1] . ' ) ');
                    }
                } elseif('between' === strtolower($val[0])) {
                    $data = explode(',',$val[1]);
                    $str .= (' ' . $key . ' BETWEEN ' . $data[0] . ' AND ' . $data[1] . ' ');
                } else {
                    $str .= '1';
                }
                if($count + 1 < $arrLen) {
                    if(isset($val[2])) {
                        $str .= (' ' . $val[2] . ' ');
                    } else {
                        $str .= ' AND ';
                    }
                }
                $count ++;
            }

            return $str;
        } else {
            return ' WHERE 1';
        }
    }

	/**
	 * 指定了按照哪个元素来分组而已
	 * @param string $group
     * @return string
	 */
	private function _group($group) {
		return empty($group) ? '' : ' GROUP BY ' . $group;
	}
	/**
	 * 比如array('test' => 'asc')，它代表的意思就是按照test递增排序：
	 * @param $order
     * @return string
	 */
	private function _order($order) {
		if(empty($order)) {
			return '';
		}
		if(is_array($order)) {
			$arr = array();
			foreach($order as $key => $val) {
				if(is_numeric($key)) {
					$arr[] = $val;
				} else {
					$arr[] = ($key . ' ' . $val);
				}
			}
			return ' ORDER BY ' . implode(',',$arr);
		}
		return ' ORDER BY ' . $order;
	}

    /**
     * 拼接 set 数组
     * @param Array|String $set
     *         要更新的数据内容，参数可以为数组也可以为字符串，建议数组。
     *         为数组时数组key为字段值，数组值为数据取值
     *         为字符串时[例：`name`='phpcms',`hits`=`hits`+1]。
     *         为数组时[例: array('name'=>'phpcms','password'=>'123456')]
     *         数组可使用array('name'=>'+=1', 'base'=>'-=1');程序会自动解析为`name` = `name` + 1, `base` = `base` - 1
     * @return string
     */
    private function _set($set){
        $field = '';
        if(is_string($set) && $set != '') {
            $field = ' SET ' . $set;
        } else if (is_array($set) && count($set) > 0) {
            $fields = array();
            foreach($set as $key => $val){
                switch (substr($val, 0, 2)) {
                    case "+=":
                        $val = substr($val,2);
                        if (is_numeric($val)) {
                            $fields[] = $this->add_special_char($key).'='.$this->add_special_char($key).'+'.$this->escape_string($val, '', false);
                        } else {
                            continue;
                        }

                        break;
                    case '-=':
                        $val = substr($val,2);
                        if (is_numeric($val)) {
                            $fields[] = $this->add_special_char($key).'='.$this->add_special_char($key).'-'.$this->escape_string($val, '', false);
                        } else {
                            continue;
                        }
                        break;
                    default:
                        $fields[] = $this->add_special_char($key).'='.$this->escape_string($val);
                }
            }
            $field = ' SET ' . implode(',', $fields);
        }
        Logger::log("[Info] _set  : " . $field);
        return $field;
    }

    /**
     * @param Array $data 只能是数组
     * @return string
     */
    private function _insert($data){

        if(is_array($data)  && count($data) > 0) {

            $fielddata = array_keys($data);
            $valuedata = array_values($data);
            array_walk($fielddata, array($this, 'add_special_char'));
            array_walk($valuedata, array($this, 'escape_string'));

            $field = implode (',', $fielddata);
            $value = implode (',', $valuedata);

            return " (" . $field . ") VALUES (" . $value . ")";
        }
        return ' ';
    }

    /**
     * 解析符号：eq,neq,gt,gte等，我们需要将它解析成为如：=,!=,>,>=等
     * @param $exp
     * @return array|mixed
     */
    private function _parseCompExp($exp) {
        if(is_string($exp)) {
            return str_replace(array_keys($this->_compExp),array_values($this->_compExp),$exp);
        }elseif(is_array($exp)) {
            $tmpArr = array();
            foreach($exp as $val) {
                $tmpArr[] = str_replace(array_keys($this->_compExp),array_values($this->_compExp),$val);
            }
            return $tmpArr;
        } else {
            //不能解析，直接返回
            return $exp;
        }
    }


    /**
     * 对字段两边加反引号，以保证数据库安全
     * @param $value 数组值
     * @return string
     */
    public function add_special_char(&$value) {
        if('*' == $value || false !== strpos($value, '(') || false !== strpos($value, '.') || false !== strpos ( $value, '`')) {
            //不处理包含* 或者 使用了sql方法。
        } else {
            $value = '`'.trim($value).'`';
        }
        if (preg_match("/\b(select|insert|update|delete)\b/i", $value)) {
            $value = preg_replace("/\b(select|insert|update|delete)\b/i", '', $value);
        }
        return $value;
    }

    /**
     * 对字段值两边加引号，以保证数据库安全
     * @param $value 数组值
     * @param $key 数组key
     * @param $quotation
     * @return string
     */
    public function escape_string(&$value, $key='', $quotation = 1) {
        if ($quotation) {
            $q = '\'';
        } else {
            $q = '';
        }
        $value = $q.$value.$q;
        return $value;
    }

}